package com.wsfPro.security.config;

import com.wsfPro.controllers.dto.system.userDetails.UserDetailsImpl;
import com.wsfPro.security.SecurityConfig;
import com.wsfPro.services.system.SysUserServiceImpl;
import com.wsfPro.util.StringUtils;
import com.wsfPro.util.jwt.JwtUtil;
import com.wsfPro.util.jwt.JwttokenconfigEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/** 认证jwtTowken 建立安全上下文（security context） */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
  Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);

  @Resource(name = "sysUserServiceImpl")
  private SysUserServiceImpl sysUserServiceImpl;

  @Autowired private JwtUtil jwtUtil;

  private SessionRegistry sessionRegistry = new SessionRegistryImpl();

  @Override
  protected void doFilterInternal(
      HttpServletRequest request, HttpServletResponse response, FilterChain chain)
      throws ServletException, IOException {
    response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
    response.setHeader("Access-Control-Allow-Credentials", "true");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "Authorization");
    // 当session没被禁用时能一直拿到登陆实体
    //    Authentication authenticationTest =
    // SecurityContextHolder.getContext().getAuthentication();
    //    if (authenticationTest != null) {
    //      log.info("SecurityContext.getAuthentication:" + authenticationTest.toString());
    //    }
    log.info("url:{}", request.getRequestURL().toString());
    // 请求头为 Authorization
    // 请求体为 Bearer token
    String jwtToken = jwtUtil.getJwtTokenByRequestHeader(request);
    if (jwtToken != null) {
      Map tokenMap = null;
      try {
        tokenMap = jwtUtil.validateToken(jwtToken);
      } catch (Exception e) {
        //   使用 sessionRegistry设置当前登陆session过期 遍历所有SessionInformation 调用即刻过期方法为expireNow()
        //   ConcurrentSessionFilter过滤器的便会监听并执行当前的HttpSession session = request.getSession(false);
        // 如果 SessionInformation info =sessionRegistry.getSessionInformation(session.getId());不为空
        // 则再判断是否isExpired()过期如果过期则执行doLogout()踢出当前登录者，就是移除该Authentication
        // —UsernamePasswordAuthenticationToken
        //        shotOff(SecurityContextHolder.getContext());
        //        这里直接抛出异常会死循环，能被当前filter拦截下来，只有在最后的UrlAccessDecisionManager抛出的才不会
        //        throw new BadCredentialsException("未登录");
        //        如何同步redis裡面的Token過時
        //        jwtUtil.removeJwtTokenByRedisHash(jwtToken);
        chain.doFilter(request, response);
        return;
      }
      if (tokenMap != null) {
        String jti = tokenMap.get(JwttokenconfigEnum.jti.getValue()).toString();
        String redisJwtTokenSession = jwtUtil.getJwtTokenByRedisHash(jti);
        if (redisJwtTokenSession != null) {

          //      当前登陆者jwtToken与redis缓存的jwtTokensession不一致说明已经被再次登录当前token失效,则设置当前登陆者为匿名
          if (redisJwtTokenSession.equals(jwtToken)) {

            //        这里还有个问题如果某次请求在刷新时间前一刻，
            // 如果在下次请求的时间过了刷新时间同时也是到了该towken的过期时间，
            // 则会让人觉得刚操作不久怎么就会话过期了
            String refreshTowken = jwtUtil.refreshToken(tokenMap);
            if (StringUtils.isNotBlank(refreshTowken)) {
              //          jwtUtil.removeJwtTokenByRedisHash(jti);
              jwtToken = refreshTowken;
              jwtUtil.addJwtTokenToRedisHash(jti, jwtToken);
            }
            response.setHeader(
                jwtUtil.getJwttokenconfigBean().getHeader(),
                jwtUtil.getJwttokenconfigBean().getTokenHead() + jwtToken);
            String username =
                StringUtils.toStringByObject(tokenMap.get(JwttokenconfigEnum.sub.getValue()));
            if (username != null
                && SecurityContextHolder.getContext().getAuthentication() == null) {
              //          UserDetails userDetails = sysUserServiceImpl.loadUserByUsername(username);
              //          不查数据库改为直接将towken转为UserDetails
              UserDetails userDetails = UserDetailsImpl.createUserDetailByMap(tokenMap);
              if (userDetails != null) {
                SecurityConfig.setAuthenticationToSecurityContext(userDetails, request);
                //            UsernamePasswordAuthenticationToken authentication =
                //                    new UsernamePasswordAuthenticationToken(
                //                            userDetails, userDetails.getPassword(),
                // userDetails.getAuthorities());
                //            authentication.setDetails(new
                // WebAuthenticationDetailsSource().buildDetails(request));
                //            SecurityContextHolder.getContext().setAuthentication(authentication);
              }
            }
          }
        }
      }
    }
    chain.doFilter(request, response);
  }
  // 禁用session便不需要即时使当前登录者过期了
  //      /** 把当前用户踢出系统 */
  //      public void shotOff(SecurityContext context) {
  //        Authentication authentication=context.getAuthentication();
  //        if(authentication!=null) {
  //          List<SessionInformation> sessionInformations =
  //                  sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
  //          for (SessionInformation sessionInformation : sessionInformations) {
  //            sessionInformation.expireNow();
  //
  //            //					.getSessionId());      //
  // sessionRegistry.removeSessionInformation(sessionInformation
  //            //      ////					.getSessionId());
  //          }
  //    }
  //  }
}
